# -*- coding: utf-8 -*-
"""
LLM基类定义模块

本模块定义了大语言模型(LLM)的抽象基类，为不同的LLM实现提供统一的接口。
采用抽象基类模式的好处：
1. 统一接口：所有LLM实现都遵循相同的接口规范
2. 可扩展性：可以轻松添加新的LLM提供商支持
3. 类型安全：通过抽象方法确保子类实现必要的功能
4. 多态性：可以在运行时切换不同的LLM实现
"""
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional, Union
from loguru import logger


class BaseLLM(ABC):
    """
    大语言模型抽象基类
    
    这个基类定义了所有LLM实现必须遵循的接口规范。
    子类需要实现所有抽象方法来提供具体的LLM功能。
    
    设计原则：
    1. 接口统一：所有LLM都提供相同的调用方式
    2. 配置灵活：支持通过kwargs传递特定于实现的配置
    3. 错误处理：提供统一的错误处理机制
    4. 日志记录：集成日志系统便于调试和监控
    
    Attributes:
        model_name (str): 模型名称标识
        config (Dict[str, Any]): 模型配置参数
    """
    
    def __init__(self, model_name: str, **kwargs):
        """
        初始化LLM基类
        
        Args:
            model_name (str): 模型名称，用于标识和日志记录
            **kwargs: 模型特定的配置参数，如温度、最大token数等
        
        Examples:
            >>> llm = SomeLLMImplementation(
            ...     model_name="gpt-3.5-turbo",
            ...     temperature=0.7,
            ...     max_tokens=2048
            ... )
        """
        self.model_name = model_name
        self.config = kwargs
        
        # 记录模型初始化信息
        logger.info(f"初始化LLM模型: {model_name}")
        logger.debug(f"模型配置: {kwargs}")
    
    @abstractmethod
    def generate(self, prompt: str, **kwargs) -> str:
        """
        生成文本响应
        
        这是LLM的核心功能，根据输入的提示词生成相应的文本。
        
        Args:
            prompt (str): 输入的提示词或问题
            **kwargs: 生成参数，如temperature、max_tokens等
                     这些参数会覆盖初始化时的默认配置
        
        Returns:
            str: 生成的文本响应
        
        Raises:
            NotImplementedError: 子类必须实现此方法
            ValueError: 当prompt为空或无效时
            RuntimeError: 当模型调用失败时
        
        Examples:
            >>> response = llm.generate("什么是人工智能？")
            >>> print(response)
            "人工智能是计算机科学的一个分支..."
        """
        pass
    
    @abstractmethod
    def batch_generate(self, prompts: List[str], **kwargs) -> List[str]:
        """
        批量生成文本响应
        
        对多个提示词进行批量处理，提高处理效率。
        某些LLM提供商支持批量API调用，可以显著提升性能。
        
        Args:
            prompts (List[str]): 提示词列表
            **kwargs: 生成参数，应用于所有提示词
        
        Returns:
            List[str]: 对应每个提示词的生成结果列表
                      结果列表的长度应与输入列表相同
        
        Raises:
            NotImplementedError: 子类必须实现此方法
            ValueError: 当prompts为空或包含无效元素时
            RuntimeError: 当批量调用失败时
        
        Examples:
            >>> prompts = ["什么是AI？", "什么是ML？"]
            >>> responses = llm.batch_generate(prompts)
            >>> len(responses) == len(prompts)
            True
        """
        pass
    
    @abstractmethod
    def get_model_info(self) -> Dict[str, Any]:
        """
        获取模型信息
        
        返回模型的详细信息，包括模型名称、版本、能力等。
        这些信息对于调试、监控和用户界面显示很有用。
        
        Returns:
            Dict[str, Any]: 包含模型信息的字典，建议包含以下字段：
                - name: 模型名称
                - version: 模型版本
                - provider: 提供商名称
                - max_tokens: 最大token限制
                - supported_languages: 支持的语言列表
                - capabilities: 模型能力描述
        
        Raises:
            NotImplementedError: 子类必须实现此方法
            RuntimeError: 当无法获取模型信息时
        
        Examples:
            >>> info = llm.get_model_info()
            >>> print(info['name'])
            "deepseek-r1:32b"
        """
        pass
    
    def validate_prompt(self, prompt: str) -> bool:
        """
        验证提示词的有效性
        
        检查提示词是否符合基本要求，如非空、长度限制等。
        子类可以重写此方法来添加特定的验证逻辑。
        
        Args:
            prompt (str): 待验证的提示词
        
        Returns:
            bool: 提示词是否有效
        
        Examples:
            >>> llm.validate_prompt("有效的提示词")
            True
            >>> llm.validate_prompt("")
            False
        """
        if not prompt or not isinstance(prompt, str):
            return False
        
        # 检查提示词长度（可根据具体模型调整）
        if len(prompt.strip()) == 0:
            return False
        
        # 检查是否包含过长的内容（简单检查）
        if len(prompt) > 100000:  # 100K字符限制
            logger.warning(f"提示词过长: {len(prompt)} 字符")
            return False
        
        return True
    
    def get_config(self) -> Dict[str, Any]:
        """
        获取当前配置
        
        Returns:
            Dict[str, Any]: 当前的配置字典副本
        """
        return self.config.copy()
    
    def update_config(self, **kwargs) -> None:
        """
        更新配置参数
        
        Args:
            **kwargs: 要更新的配置参数
        
        Examples:
            >>> llm.update_config(temperature=0.8, max_tokens=1024)
        """
        self.config.update(kwargs)
        logger.debug(f"更新模型配置: {kwargs}")
    
    def __str__(self) -> str:
        """
        返回模型的字符串表示
        
        Returns:
            str: 模型的描述字符串
        """
        return f"{self.__class__.__name__}(model_name='{self.model_name}')"
    
    def __repr__(self) -> str:
        """
        返回模型的详细字符串表示
        
        Returns:
            str: 模型的详细描述字符串
        """
        return f"{self.__class__.__name__}(model_name='{self.model_name}', config={self.config})"